package com.googlecode.lookie.explorer.internal;

import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.ActionCommand;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.settings.Settings;
import org.springframework.richclient.tree.TreeSelectionListenerSupport;
import org.springframework.richclient.util.PopupMenuMouseListener;

import com.googlecode.lookie.explorer.DirectorySelectionEvent;
import com.googlecode.lookie.platform.settings.LookieSettingsManager;
import com.googlecode.lookie.swing.laf.LookAndFeelConfigurer;
import com.googlecode.lookie.swing.tree.model.DeferredTreeNodeModel;
import com.googlecode.lookie.swing.tree.navigation.Navigator;
import com.googlecode.lookie.swing.tree.render.LabelProviderTreeCellRenderer;
import com.googlecode.lookie.swing.tree.support.file.FileTreeNode;
import com.googlecode.lookie.swing.tree.support.file.FileTreeNodeLabelProvider;

public class ExplorerView
    extends AbstractView
    implements PropertyChangeListener
{

    private static FileSystemView fsv = FileSystemView.getFileSystemView();

    private Map<String, Image> iconCache = new HashMap<String, Image>();

    private JTree tree;

    private Navigator navigator;

    private FileTreeNodeLabelProvider labelProvider;

    private JPopupMenu popupContextMenu;

    private RefreshCommand refreshCommand;

    private GoIntoCommand goIntoCommand;

    private GoUpCommand goUpCommand;

    private GoHomeCommand goHomeCommand;

    private String[] initialExpandedPaths;

    public LookieSettingsManager getSettingsManager()
    {
        return (LookieSettingsManager) getApplicationServices().getService( LookieSettingsManager.class );
    }

    public LookAndFeelConfigurer getLookAndFeelConfigurer()
    {
        return (LookAndFeelConfigurer) getApplicationServices().getService( LookAndFeelConfigurer.class );
    }

    public void setInitialExpandedPaths( String[] initialExpandedPaths )
    {
        this.initialExpandedPaths = initialExpandedPaths;
    }

    @Override
    protected JComponent createControl()
    {
        initIcons();

        final FileSystemTreeNode root = new FileSystemTreeNode();

        final FileSystemTreeNodeProvider nodeProvider = new FileSystemTreeNodeProvider();

        Settings userSettings = getSettingsManager().getUserSettings();
        boolean useNativeIcons = userSettings.getBoolean( SettingsIds.USE_NATIVE_ICONS );

        userSettings.addPropertyChangeListener( SettingsIds.USE_NATIVE_ICONS, this );

        labelProvider = new FileTreeNodeLabelProvider( useNativeIcons )
        {

            @Override
            protected Icon getComputerNodeIcon( FileTreeNode node )
            {
                return new ImageIcon( iconCache.get( "computer" ) );
            }

            @Override
            protected Icon getDirectoryNodeIcon( FileTreeNode node )
            {
                return new ImageIcon( iconCache.get( "directory" ) );
            }

            @Override
            protected Icon getDriveNodeIcon( FileTreeNode node )
            {
                return new ImageIcon( iconCache.get( "drive" ) );
            }

        };

        tree = new JTree();
        navigator = new FileSystemTreeNavigator( tree, root, nodeProvider );

        tree.setRootVisible( false );
        tree.setShowsRootHandles( true );

        LabelProviderTreeCellRenderer renderer = new LabelProviderTreeCellRenderer(
                                                                                    labelProvider,
                                                                                    getLookAndFeelConfigurer()
                                                                                        .createDefaultTreeCellRenderer() );
        tree.setCellRenderer( renderer );

        refreshCommand = new RefreshCommand();
        goIntoCommand = new GoIntoCommand();
        goUpCommand = new GoUpCommand();
        goHomeCommand = new GoHomeCommand();

        tree.addMouseListener( new PopupMenuMouseListener()
        {

            protected JPopupMenu getPopupMenu()
            {
                return getPopupContextMenu();
            }

        } );

        tree.addTreeSelectionListener( new TreeSelectionListenerSupport()
        {

            @Override
            protected void onSingleSelection( TreePath selectedPath )
            {
                FileSystemTreeNode node = (FileSystemTreeNode) selectedPath.getLastPathComponent();
                getApplicationContext().publishEvent( new DirectorySelectionEvent( ExplorerView.this, node.getFile() ) );
            }

        } );

        JScrollPane scrollPane = getComponentFactory().createScrollPane( tree );

        if ( initialExpandedPaths != null )
        {
            for ( String path : initialExpandedPaths )
            {
                if ( path != null )
                {
                    File file = new File( path );
                    if ( file.exists() )
                    {
                        expandPath( file );
                    }
                }
            }
        }

        return scrollPane;
    }

    private void initIcons()
    {
        iconCache.put( "computer", createImage( "computer.png" ) );
        iconCache.put( "directory", createImage( "folder.png" ) );
        iconCache.put( "drive", createImage( "drive.png" ) );
    }

    private Image createImage( String name )
    {
        Image image = null;
        try
        {
            image = ImageIO.read( getClass().getClassLoader().getResourceAsStream( "images/" + name ) );
        }
        catch ( IOException e )
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return image;
    }

    private void expandPath( File file )
    {
        String[] path = getPath( file );
        DeferredTreeNodeModel model = (DeferredTreeNodeModel) tree.getModel();
        model.setDeferredEnabled( false );
        FileSystemTreeNode current = (FileSystemTreeNode) model.getRoot();
        TreePath treePath = new TreePath( current );
        for ( String p : path )
        {
            for ( int i = 0; i < model.getChildCount( current ); i++ )
            {
                FileSystemTreeNode child = (FileSystemTreeNode) model.getChild( current, i );
                if ( fsv.getSystemDisplayName( child.getFile() ).equals( p ) )
                {
                    current = child;
                    break;
                }
            }

            treePath = treePath.pathByAddingChild( current );

        }
        tree.expandPath( treePath );
        model.setDeferredEnabled( true );
    }

    private String[] getPath( File file )
    {
        List<String> path = new ArrayList<String>();
        addToPath( file, path );
        return path.toArray( new String[path.size()] );
    }

    private void addToPath( File file, List<String> path )
    {
        if ( file == null )
        {
            return;
        }
        path.add( 0, fsv.getSystemDisplayName( file ) );
        File parent = fsv.getParentDirectory( file );
        addToPath( parent, path );
    }

    private boolean isNothingSelected()
    {
        return tree.getSelectionCount() == 0;
    }

    private JPopupMenu getPopupContextMenu()
    {
        if ( popupContextMenu == null )
        {
            CommandGroup group = getWindowCommandManager()
                .createCommandGroup(
                                     "explorerViewPopupContextMenuCommandGroup",
                                     new Object[] {
                                         refreshCommand,
                                         "separator",
                                         goIntoCommand,
                                         "separator",
                                         goUpCommand,
                                         goHomeCommand } );
            popupContextMenu = group.createPopupMenu();
        }
        updateCommands();
        return popupContextMenu;
    }

    private void updateCommands()
    {
        refreshCommand.setEnabled( !isNothingSelected() );
        goIntoCommand.setEnabled( !isNothingSelected() );

        goUpCommand.setEnabled( navigator.canGoUp() );
    }

    public void propertyChange( final PropertyChangeEvent evt )
    {
        SwingUtilities.invokeLater( new Runnable()
        {

            public void run()
            {
                Boolean useNativeIcons = (Boolean) evt.getNewValue();
                labelProvider.setUseNativeIcons( useNativeIcons );
                tree.repaint();
            }

        } );
    }

    private class GoIntoCommand
        extends ActionCommand
    {

        public GoIntoCommand()
        {
            super( "goIntoCommand" );
        }

        @Override
        protected void doExecuteCommand()
        {
            TreePath selectionPath = tree.getSelectionPath();
            if ( selectionPath.getLastPathComponent() instanceof FileSystemTreeNode )
            {
                FileSystemTreeNode node = (FileSystemTreeNode) selectionPath.getLastPathComponent();
                navigator.goTo( node );
            }
        }

    }

    private class GoUpCommand
        extends ActionCommand
    {

        public GoUpCommand()
        {
            super( "goUpCommand" );
        }

        @Override
        protected void doExecuteCommand()
        {
            navigator.goUp();
        }

    }

    private class GoHomeCommand
        extends ActionCommand
    {

        public GoHomeCommand()
        {
            super( "goHomeCommand" );
        }

        @Override
        protected void doExecuteCommand()
        {
            navigator.goHome();
        }

    }

    private class RefreshCommand
        extends ActionCommand
    {

        public RefreshCommand()
        {
            super( "refreshCommand" );
        }

        @Override
        protected void doExecuteCommand()
        {
            TreePath selectionPath = tree.getSelectionPath();
            if ( selectionPath.getLastPathComponent() instanceof FileSystemTreeNode )
            {
                FileSystemTreeNode node = (FileSystemTreeNode) selectionPath.getLastPathComponent();
                node.clear();
                update( node );
            }
        }

        private void update( FileSystemTreeNode node )
        {
            if ( tree.getModel() instanceof DefaultTreeModel )
            {
                ( (DefaultTreeModel) tree.getModel() ).nodeStructureChanged( node );
            }
        }

    }

}
